#include "aceMesh_runtime_c.h"
#include <stdlib.h>
#include <stdio.h>
#include "slave.h"
#include "3d7p_aceMesh.h"
#include "task.h"
#include "ldm_malloc.h"
__thread_local volatile unsigned long get_reply,put_reply;

__thread_local double A_slave[2][BLKX+2][BLKY+2][BLKZ+2];

extern double A[2][SIZEX][SIZEY][SIZEZ];

__thread_local_fix int amt_flag = 0;


#ifdef _PROFILING
__thread_local int core_id=0;
__thread_local volatile unsigned long start,end;
__thread_local volatile unsigned long dma_start,dma_end;
__thread_local volatile unsigned long comp_st,comp_et;
__thread_local volatile unsigned long dma_et,dma_st,dma_et_,dma_st_;
extern unsigned long slave_dma_count[64];
extern unsigned long slave_comp_count[64];
extern unsigned long slave_comp_dma_count[64];
unsigned long rtc_()
{
  unsigned long rpcc;
  asm volatile("rcsr  %0,4":"=r"(rpcc));
  return rpcc;
}
#endif

#define alpha_d 0.0876
#define beta_d  0.0765
void stencil_core(targs* args)                                      
{  
   //0. assumptions       
   //data region: [starti-1,endi] [startj-1,endj] [startk-1,endk]  
   //compute region: [starti, endi) [startj,endj) [startk,endk)
   long  array_size;
   int dest,src,i,j,k,s_iter,s_starti,s_endi,s_startj,s_endj,s_startk,s_endk;
   int dma_count=0;
   int tsize, bsize, stride;
   int tileno[3];
   int varno[2];
   int reuse[2];
   int lhs_no,rhs_no;
   s_iter = args->iter;
   dest = (s_iter) % 2 ;
   src = 1 - dest;
   s_starti = args->starti;
   s_endi = args->endi;
   s_startj = args->startj;
   s_endj = args->endj;
   s_startk = 1;
   s_endk = args->k_N-1;
/*   if(amt_flag == 0)
  {
    ldm_set_datatiles(2);//total ldm array
    amt_flag = 1;
  }
*/
  tileno[0]=s_starti/BLKX;
  tileno[1]=s_startj/BLKY;
  tileno[2]=s_startk/BLKZ;
//  slave_ldm_postreq_tile(s_iter, &A[dest][0][0][0], tileno, OUT);//
  slave_ldm_postreq_tile(&A[dest][0][0][0], 3,tileno, OUT);//
//  slave_ldm_postreq_tile(s_iter, &A[src][0][0][0], tileno, IN);
  slave_ldm_postreq_tile(&A[src][0][0][0], 3, tileno, IN);
  slave_ldm_getreq_tile(varno,reuse);
  lhs_no = varno[0];
  rhs_no = varno[1];
  get_reply=0;
  if(reuse[1]==0){
    bsize=(s_endk-s_startk+2)*8;
    stride=(SIZEZ-(s_endk-s_startk+2))*8;  //stride: from blocktail to blockhead 
    tsize= bsize*(s_endj-s_startj+2);
    for(i = s_starti-1; i < s_endi+1; i++)
    {   
       athread_get(PE_MODE,&A[src][i][s_startj-1][s_startk-1]
                 , &A_slave[rhs_no][(i-s_starti+1)][0][0]
                 , tsize, &get_reply
                 , 0, stride, bsize);
    }   
    dma_count=(s_endi-s_starti+2);
  }else{
// fetch data
    //up down
     stride = (SIZEZ-(s_endk-s_startk+2))*8;
     tsize = (s_endj-s_startj)*(s_endk-s_startk+2)*8;
     bsize = (s_endk-s_startk+2)*8;
     athread_get(PE_MODE,&A[src][s_starti-1][s_startj][s_startk-1]
                 , &A_slave[rhs_no][0][1][0]
                 , tsize, &get_reply
                 , 0, stride, bsize);
     athread_get(PE_MODE,&A[src][s_endi][s_startj][s_startk-1]
                 , &A_slave[rhs_no][s_endi-s_starti+1][1][0]
                 , tsize, &get_reply
                 , 0, stride, bsize);
    //left right
     for(i = s_starti; i < s_endi; ++i)
     {
       athread_get(PE_MODE,&A[src][i][s_startj-1][s_startk]
                   , &A_slave[rhs_no][i-s_starti+1][0][1]
                   , (s_endk-s_startk)*8,&get_reply,0,0,0);
       athread_get(PE_MODE,&A[src][i][s_endj][s_startk]
                   , &A_slave[rhs_no][(i-s_starti+1)][s_endj-s_startj+1][1]
                   , (s_endk-s_startk)*8,&get_reply,0,0,0);
     }
     //front back
     for(i = s_starti; i < s_endi; ++i )
       for(j = s_startj; j < s_endj; ++j)
       {
          athread_get(PE_MODE,&A[src][i][j][s_startk-1]
                   , &A_slave[rhs_no][i-s_starti+1][j-s_startj+1][0]
                   , 8,&get_reply,0,0,0);
          athread_get(PE_MODE,&A[src][i][j][s_endk]
                   , &A_slave[rhs_no][(i-s_starti+1)][(j-s_startj+1)][(s_endk-s_startk+1)]
                   , 8,&get_reply,0,0,0); 
      }
     dma_count+=(2+(s_endi-s_starti)*2+(s_endi-s_starti)*(s_endj-s_startj)*2);
  }
  while(get_reply != dma_count);
//2. computation
    for(i = 1; i < s_endi-s_starti+1; i++)
       for(j = 1; j < s_endj-s_startj+1; j++)
         for(k = 1; k < s_endk-s_startk+1; k++) 
            A_slave[lhs_no][i][j][k] = alpha_d * (A_slave[rhs_no][i][j][k]) + \
                     beta_d * (A_slave[rhs_no][(i-1)][j][k] + A_slave[rhs_no][(i+1)][j][k] +\
                     A_slave[rhs_no][i][j-1][k] + A_slave[rhs_no][i][(j+1)][k]+\
                     A_slave[rhs_no][i][j][(k-1)] + A_slave[rhs_no][i][j][(k+1)]);  
//4.1 write back
   put_reply=0;
   for(i =  s_starti; i < s_endi; ++i)
     for(j =  s_startj; j < s_endj; ++j)  
       athread_put(PE_MODE,&A_slave[lhs_no][(i-s_starti+1)][(j-s_startj+1)][1]
                     , &A[dest][i][j][s_startk]
                     ,(s_endk-s_startk)*8,&put_reply,0,0);
//4.2 synchronize, wait for writing back finish.
   while(put_reply!=(s_endi-s_starti)*(s_endj-s_startj));
   
}
